#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/err.h>
#include <linux/compiler.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/arch/regs-gpio.h>
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/poll.h>



#define CTRL_LED_CMD    (0x01)

static int dev_open(struct inode *, struct file *);
static ssize_t dev_read(struct file *, char __user *, size_t , loff_t *);
static ssize_t dev_write(struct file *, const char __user *, size_t , loff_t *);
static int dev_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
static unsigned int dev_poll(struct file *, struct poll_table *);

static int dev_release(struct inode *, struct file *);

static void keys_timer_timeout(unsigned long data);

typedef unsigned int    uint32;
struct pindesc
{
    uint32          pin;
    uint32          id;
};
struct keyinfo
{
    char            *name;
    struct pindesc  pin;
    uint32          irqno;
    int             flags;
};

static struct keyinfo   keysinfo[] =
{
    [0] = {.name  = "eint0"  , .pin  = { .pin = S3C2410_GPF0,  .id = 0 }, .irqno  = IRQ_EINT0  , .flags = -1},
    [1] = {.name  = "eint2"  , .pin  = { .pin = S3C2410_GPF2,  .id = 1 }, .irqno  = IRQ_EINT2  , .flags = -1},
    [2] = {.name  = "eint11" , .pin  = { .pin = S3C2410_GPG3,  .id = 2 }, .irqno  = IRQ_EINT11 , .flags = -1},
    [3] = {.name  = "eint19" , .pin  = { .pin = S3C2410_GPG11, .id = 3 }, .irqno  = IRQ_EINT19 , .flags = -1}
};

static int g_key_index = -1;
static unsigned char g_key_val;
static DECLARE_WAIT_QUEUE_HEAD(g_wait_queue);
static int key_event = 0;


struct mydev
{
	uint32 			        major;
	char 					*dev_name;
	char 					*cls_name;
	char 					*cls_dev_name;
	struct class 			*cls;
	struct class_device 	*cls_dev;
	int 					flags;
	struct file_operations  dev_oper;
    struct timer_list       keys_timer;

    volatile unsigned long  phy_addr;
    volatile unsigned long  *virt_addr;
    uint32                  addr_len;
    volatile unsigned long  *gpfcon;
    volatile unsigned long  *gpfdat;
};

static struct mydev dev = {
    .major    = 0,
	.dev_name = "keys_dev",         //cat /proc/devices 可以通过此名找打分配的主设备号
	.cls_name = "keys",             //创建目录：/sys/class/keys
	.cls_dev_name = "key",          //创建目录：/sys/class/keys/key , mdev会用这个名字创建设备节点名/dev/key
	.cls = NULL,
	.cls_dev = NULL,
	.flags = 0,
	.dev_oper = {
		.owner		= THIS_MODULE,
		.open 		= dev_open,
		.read 		= dev_read,
		.write 		= dev_write,
		.ioctl      = dev_ioctl,
		.poll       = dev_poll,
		.release	= dev_release, 
	},
	.keys_timer     = TIMER_INITIALIZER(keys_timer_timeout, 10, keysinfo),

    .phy_addr       = 0,
    .virt_addr      = NULL,
    .addr_len        = 0,
};

/*          eint0       eint2       eint11      eint19
 *按下:       0x00        0x01        0x02        0x03
 *松开:       0x80        0x81        0x82        0x83
 */
static void keys_timer_timeout(unsigned long data)
{
    if(g_key_index == -1)
        return;
    
    struct keyinfo *keysinfo = (struct keyinfo *)data;
    struct keyinfo devinfo = keysinfo[g_key_index];
    
    uint32 pinval = s3c2410_gpio_getpin(devinfo.pin.pin);
    if(!pinval) //按下
    {
        printk(KERN_DEBUG "The key[%s] is pressed\n", devinfo.name);
        g_key_val = devinfo.pin.id;
    }
    else
    {
        printk(KERN_DEBUG "The key[%s] is released\n", devinfo.name);
        g_key_val = (0x80 | devinfo.pin.id);
    }
    //唤醒休眠的read
    key_event = 1;
    wake_up_interruptible(&g_wait_queue);
}

static irqreturn_t keys_irq_handler(int irqno, void *dev_id)
{
    irqreturn_t irq_ret = IRQ_HANDLED;
    struct keyinfo *devinfo = (struct keyinfo *)dev_id;
    g_key_index =  devinfo->pin.id;
    /* jiffies: 
     *   Linux内核使用全局变量jiffies记录系统自从启动以来的滴答数。在系统启动的时初始化jiffies为0，
     *   在每个时钟中断处理例程中该值会加1。假如HZ=1000，每隔1ms系统会发生一次时钟中断，
     *   也就是说每隔1ms jiffies的值会加1，也就是说1s内jiffies的值也是HZ，所以系统启动的时间就是： jiffies/HZ
     * mod_timer: 延长定时器的超时值（在10ms之内如果按键产生多次中断，那么也只处理一次）
     * 
     */
    mod_timer(&dev.keys_timer, jiffies+HZ/100);
    
    return IRQ_RETVAL(irq_ret);
}


/*
 * @func: device open
 */
static int dev_open(struct inode *node, struct file *filep)
{
	printk(KERN_DEBUG "open device\n");
    return 0;
}

/*
 * @func: device read
 */
static ssize_t dev_read(struct file *filep, char __user *buffer, size_t size, loff_t *offset)
{
    printk(KERN_DEBUG "read device\n");
    if(size != 1 || !key_event)
        return -EINVAL;
    	
    if(key_event)
    {
        copy_to_user(buffer, &g_key_val, 1);
        key_event = 0;
    }
	return 0;
}

/*
 * @func: device write
 */
static ssize_t dev_write(struct file *filep, const char __user *buffer, size_t size, loff_t *offset)
{
	printk(KERN_DEBUG "write device\n");
	return 0;
}

/*
 * device ioctl
 */
static int dev_ioctl(struct inode *node, struct file *filep, unsigned int cmd, unsigned long arg)
{
    printk(KERN_DEBUG "ioctl device...");
    return 0;
}

static unsigned int dev_poll(struct file *filep, poll_table *table)
{
    unsigned int mask = 0;
    //[0]. 将当前线程放入到休眠队列表中
    poll_wait(filep, &g_wait_queue, table);
    if(key_event) 
        mask = POLLIN | POLLRDNORM;
    return mask;
}


/*
 * @func: device close
 */

static int dev_release(struct inode *node, struct file *filep)
{

	printk(KERN_DEBUG "release device\n");
	return 0;
}



static int __init keydrv_init(void)
{
    int i;
    printk(KERN_DEBUG "The driver 'keys_drv' is inserted!\n");
	//[0]. 注册一个字符设备
 	dev.major = register_chrdev(0, dev.dev_name, &dev.dev_oper);
    if(dev.major < 0)
	{
		printk(KERN_ERR "Register char device[%s] failed", dev.dev_name);
		goto ERR;
	}
	//[1]. 将字符设备信息注册到sys文件系统中
	dev.cls = class_create(THIS_MODULE, dev.cls_name);
	if(IS_ERR(dev.cls))
	{
		printk(KERN_ERR "Create class [%s] failed", dev.cls_name);
		unregister_chrdev(dev.major, dev.dev_name);
		goto ERR;
	}
	dev.cls_dev = class_device_create(dev.cls, NULL, MKDEV(dev.major, 0), NULL, "%s", dev.cls_dev_name);
	if(unlikely(IS_ERR(dev.cls_dev)))
	{
        printk(KERN_ERR "Create class device [%s] failed", dev.cls_dev_name);
        unregister_chrdev(dev.major, dev.dev_name);
	    class_destroy(dev.cls);
        goto ERR;
    }

    //[2] 注册按键中断处理程序
    for(i = 0; i < 4; ++i)
    {
        if(request_irq(keysinfo[i].irqno, keys_irq_handler,IRQT_BOTHEDGE, keysinfo[i].name, &keysinfo[i]) < 0)
           printk(KERN_WARNING "Register irq for [%s] failed!\n", keysinfo[i].name);
        keysinfo[i].flags = 0;
    }

    //[3] 初始化一个定时器，防止按键抖动
    init_timer(&dev.keys_timer);
    //[4] 启动定时器
    add_timer(&dev.keys_timer);
    
    return 0;
ERR:
    printk(KERN_ERR "The driver 'keys_drv' register failed\n");

	dev.flags = -1;
	return -1;
}
static void __exit keydrv_exit(void)
{
    int i;
    printk(KERN_DEBUG "The driver 'keys_drv' is removed!\n");
	if(!dev.flags)
	{
		unregister_chrdev(dev.major, dev.dev_name);
        class_device_unregister(dev.cls_dev);
		class_destroy(dev.cls);

        for(i = 0; i < 4; ++i)
        {
            if(!keysinfo[i].flags)
                free_irq(keysinfo[i].irqno, &keysinfo[i]);
            keysinfo[i].flags = -1;
        }

        del_timer(&dev.keys_timer);
	}
}



module_init(keydrv_init);
module_exit(keydrv_exit);


MODULE_LICENSE("GPL");

